package cn.xhh.dailyreview.server.dao;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import cn.xhh.dailyreview.server.model.Category;
import cn.xhh.dailyreview.server.model.Review;
import cn.xhh.dailyreview.server.model.User;
import cn.xhh.dailyreview.server.utils.DbUtil;

/**
 * Data Access Object for {@link Category}
 * @author cooky
 */
public class CategoryDAO {
	
	private static final Logger log = Logger.getLogger(CategoryDAO.class.getName());
	
	/**
	 * Create (or update) a category.
	 * @return <code>true</code>: operation succeeded; <code>false</code>: failed.
	 */
	@SuppressWarnings("unchecked")
	public boolean create(Category c) {
		if (c==null || c.invalid()) {
			log.severe("Creating/updating category failed - invalid: " + c);
			return false;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Creating/updating category...");
		
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		boolean result = true;
		try {
			// check if the name exists
			Query q = pm.newQuery(Category.class);
			String filter = "userId == " + c.getUserId() + " && name == pName";
			q.setFilter(filter);
			q.declareParameters("String pName");
			q.setResult("id");
			List<Long> existent = (List<Long>) q.execute(c.getName());
			if (c.getId() == null) { // is creating
				if (!existent.isEmpty()) { // category name exists for the user
					log.info("Creating category failed: name exists. Data: " + c);
					result  = false;
				}
			} else if (existent.size()==1 && !c.getId().equals(existent.get(0)) || existent.size()>1) {
				// is updating and the category name is used by another category of the user
				log.info("Updating category failed: name exists. ID: " + c.getId() + ". Data: " + c);
				result  = false;
			}
			if (result) pm.makePersistent(c);
		} finally {
			pm.close();
		}
		
		if (result && log.isLoggable(Level.FINE))
			log.fine("Category created/updated. ID: " + c.getId() + ". Data: " + c);
		return result;
	}
	
	public Category read(Long id) {
		if (id == null) {
			log.severe("Reading category failed: id is null.");
			return null;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Reading category with id: " + id);
		
		Category c = null;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			c = pm.getObjectById(Category.class, id);
		} catch (JDOObjectNotFoundException e) {
			log.warning("Reading category failed: no category found with ID: " + id);
			c = null;
		} finally {
			pm.close();
		}
		
		if (c!=null && log.isLoggable(Level.FINER))
			log.finer("Category read. ID: " + c.getId() + ". Data: " + c);
		return c;
	}
	
	/**
	 * Get the count of categories owned by the given user.
	 * @param u the owner, null for all users
	 * @return
	 */
	public int readCount(User u) {
		if (u!=null && u.getId()==null) {
			log.severe("Reading categories count of a user failed: user ID is null.");
			return -1;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Reading categories count of " + (u==null ? "all users..." : "a user... ID: " + u.getId()));
		
		int count = -1;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			Query q = pm.newQuery(Category.class);
			if (u != null)
				q.setFilter("userId == " + u.getId());
			q.setResult("count(this)");
			q.setUnique(true);
			count = (Integer) q.execute();
			q.closeAll();
		} finally {
			pm.close();
		}
		
		if (log.isLoggable(Level.FINER))
			log.finer("Categories count of user (userId: " + u.getId() + ") read: " + count);
		return count;
	}

	public boolean update(Category c) {
		return create(c);
	}
	
	/**
	 * @return <code>true</code>: operation succeeded; <code>false</code>: failed.
	 */
	public boolean delete(Category c) {
		if (c==null || c.getId()==null) {
			log.severe("Deleting category failed: " + (c==null ? "category" : "ID") + " is null.");
			return false;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Deleting category... ID: " + c.getId());
		
		boolean result = true;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			// first make sure the category is persistent and existent
			c = pm.getObjectById(Category.class, c.getId());
//			Transaction tx = pm.currentTransaction();
//			try {
//				tx.begin();
				pm.newQuery(Review.class, "categoryId == " + c.getId()).deletePersistentAll();
				pm.deletePersistent(c);
//				tx.commit();
//			} finally {
//				if (tx.isActive()) {
//					tx.rollback();
//					log.severe("Deleting category failed: rollback. ID: " + c.getId());
//					result = false;
//				}
//			}
		} catch (JDOObjectNotFoundException e) {
			log.warning("Deleting category failed: no category found with ID: " + c.getId());
			result = false;
		} finally {
			pm.close();
		}

		if (result && log.isLoggable(Level.FINE))
			log.fine("Category deleted. ID: " + c.getId() + ". Data: [ " + c + " ]");
		return result;
	}

}
